//The MIT License
//
//Copyright (c) 2009 nodchip
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in
//all copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//THE SOFTWARE.
package tv.dyndns.kishibe.client.game;

import tv.dyndns.kishibe.client.Constant;
import tv.dyndns.kishibe.client.Controller;
import tv.dyndns.kishibe.client.SceneBase;
import tv.dyndns.kishibe.client.SceneResult;
import tv.dyndns.kishibe.client.Service;
import tv.dyndns.kishibe.client.SharedData;
import tv.dyndns.kishibe.client.SoundPlayer;
import tv.dyndns.kishibe.client.UserData;
import tv.dyndns.kishibe.client.game.left.WidgetPlayer;
import tv.dyndns.kishibe.client.game.left.WidgetPlayerList;
import tv.dyndns.kishibe.client.packet.PacketGameStatus;
import tv.dyndns.kishibe.client.packet.PacketMatchingPlayer;
import tv.dyndns.kishibe.client.packet.PacketProblemData;
import tv.dyndns.kishibe.client.util.SerialTimer;

import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.Window.ClosingEvent;
import com.google.gwt.user.client.Window.ClosingHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;

public class SceneGame implements SceneBase, ClosingHandler, CloseHandler<Window> {
	private final PanelGame panel = new PanelGame();
	private final WidgetPlayerList playerList;
	private PanelQuestionBase question;
	private PacketProblemData[] problems;
	private int problemCounter = 0;
	private WidgetTimer widgetTimer;
	private final Timer timerUpdate = new SerialTimer() {
		public void run() {
			update();
		}
	};
	private int lastTransition = -1;
	private final boolean addPenalty;
	private HandlerRegistration handlerRegistrationCloseHandler = null;
	private HandlerRegistration handlerRegistrationClosingHandler = null;

	public SceneGame(PacketProblemData[] problemData, PacketMatchingPlayer[] players, boolean addPenalty) {
		problems = problemData;
		playerList = new WidgetPlayerList(players, SharedData.getInstance().getPlayerListIndex());
		panel.setPlayerList(playerList);

		Controller.getInstance().setGamePanel(panel);

		this.addPenalty = addPenalty;
	}

	public void update() {
		getGameStatus();
	}

	private void getGameStatus() {
		final int sessionId = SharedData.getInstance().getSessionId();
		final int playerListId = SharedData.getInstance().getPlayerListIndex();

		Service.Util.getInstance().getGameStatus(sessionId, playerListId, callbackGetGameStatus);
	}

	private final AsyncCallback<PacketGameStatus> callbackGetGameStatus = new AsyncCallback<PacketGameStatus>() {
		public void onSuccess(PacketGameStatus result) {
			try {
				if (result == null) {
					Controller.getInstance().showErrorMessage("SceneGame.callbackGetGameStatus - 無効なゲーム状態が返されました。");
					return;
				}

				if (result.numberOfPlayingHumans <= 1) {
					if (handlerRegistrationClosingHandler != null) {
						handlerRegistrationClosingHandler.removeHandler();
						handlerRegistrationClosingHandler = null;
					}
				}

				// 順位変動表示
				if (result.playerAnswers != null) {
					for (int i = 0; i < result.playerAnswers.length; ++i) {
						final String answer = result.playerAnswers[i];
						if (answer != null) {
							WidgetPlayer w = playerList.getWidgetPlayer(i);
							w.recieveAnswer(answer);
						}

						final WidgetPlayer w = playerList.getWidgetPlayer(i);
						w.setDestY((result.ranking[i] - 1) * WidgetPlayerList.HEIGHT);
					}
				}

				if (widgetTimer != null) {
					widgetTimer.setTimer(result.restSecond / 1000);
				}

				if (lastTransition != result.transition || result.transition == Constant.TRANSITION_QUESTION && problemCounter != result.problemCounter) {
					lastTransition = result.transition;

					switch (result.transition) {
					case Constant.TRANSITION_QUESTION: {
						// 次の問題へ
						// プレイヤー情報窓クローズ
						problemCounter = result.problemCounter;

						final PacketProblemData problem = problems[problemCounter];
						widgetTimer = new WidgetTimer();
						question = FactoryPanelQuestion.getINSTANCE().create(problem, widgetTimer);
						panel.setQuestionPanel(question);
						panel.setQuestionNumber(problemCounter);

						getPlayerNameList();

						playerList.clearAnswer();
						playerList.setProblem(problem);

						break;
					}
					case Constant.TRANSITION_ANSWER: {
						// 問題の解答発表
						// プレイヤー情報窓オープン
						question.getWidgetInput().enable(false);
						question.displayRatio();

						final PacketProblemData problem = problems[problemCounter];
						playerList.showResult();
						panel.setScore(result.playerScore);

						// 正答数更新
						final int genre = problem.genre;
						final int type = problem.type;
						final int randomFlag = problem.randomFlag;
						final int playerListIndex = SharedData.getInstance().getPlayerListIndex();
						final WidgetPlayer widgetPlayer = playerList.getWidgetPlayer(playerListIndex);
						final boolean isCorrect = widgetPlayer.getIsCorrect();
						final int[][][] correctCount = UserData.getInstance().getCorrectCount();
						final int goodBadIndex = isCorrect ? 0 : 1;
						++correctCount[genre][type][goodBadIndex];
						++correctCount[0][type][goodBadIndex];
						++correctCount[genre][0][goodBadIndex];
						++correctCount[0][0][goodBadIndex];
						++correctCount[genre][Constant.TYPE_RANDOM_FLAG_OFFSET + randomFlag][goodBadIndex];
						++correctCount[0][Constant.TYPE_RANDOM_FLAG_OFFSET + randomFlag][goodBadIndex];

						// サウンド再生
						if (!widgetPlayer.isAnswerRecieved()) {
							SoundPlayer.getInstance().play(Constant.SOUND_URL_TIME_UP);
						} else if (problem.isCorrect(widgetPlayer.getMyAnswer())) {
							SoundPlayer.getInstance().play(Constant.SOUND_URL_GOOD);
						} else {
							SoundPlayer.getInstance().play(Constant.SOUND_URL_BAD);
						}

						break;
					}
					case Constant.TRANSITION_RESULT: {
						// リザルト画面へ遷移
						timerUpdate.cancel();
						Controller.getInstance().setScene(new SceneResult(problems));
						break;
					}
					default:
						break;
					}
				}
			} catch (Exception e) {
				Controller.getInstance().showErrorMessage("エラーが発生しました。管理人に連絡してください。(1)");
				e.printStackTrace();
			}
		}

		public void onFailure(Throwable caught) {
			Controller.getInstance().showErrorMessage("SceneGame.callbackGetGameStatus - ゲーム状態の取得に失敗しました。");
		}
	};

	private void getPlayerNameList() {
		final int sessionId = SharedData.getInstance().getSessionId();
		Service.Util.getInstance().getPlayerNameList(sessionId, callbackGetPalyerNameList);
	}

	private final AsyncCallback<String[]> callbackGetPalyerNameList = new AsyncCallback<String[]>() {
		public void onSuccess(String[] result) {
			try {
				if (result == null) {
					Controller.getInstance().showErrorMessage("SceneGame.callbackGetPalyerNameList - 無効なプレイヤー名リスト情報が返されました。");
					return;
				}

				for (int i = 0; i < result.length; ++i) {
					if (result[i] == null) {
						result[i] = "未初期化です";
					}
				}
				playerList.setPlayerName(result);
			} catch (Exception e) {
				Controller.getInstance().showErrorMessage("エラーが発生しました。管理人に連絡してください。(2)");
				Controller.getInstance().showErrorMessage(e.getMessage());
			}
		}

		public void onFailure(Throwable caught) {
			Controller.getInstance().showErrorMessage("SceneGame.callbackGetPalyerNameList - プレイヤー名リストの取得に失敗しました。");
		}
	};

	public void onSendAnswer() {
		playerList.openAnswer();
	}

	public void onLoad() {
		timerUpdate.scheduleRepeating(1000);
		if (addPenalty) {
			handlerRegistrationCloseHandler = Window.addCloseHandler(this);
			handlerRegistrationClosingHandler = Window.addWindowClosingHandler(this);
		}
	}

	public void onUnload() {
		if (addPenalty) {
			if (handlerRegistrationCloseHandler != null) {
				handlerRegistrationCloseHandler.removeHandler();
				handlerRegistrationCloseHandler = null;
			}
			if (handlerRegistrationClosingHandler != null) {
				handlerRegistrationClosingHandler.removeHandler();
				handlerRegistrationClosingHandler = null;
			}
		}
		timerUpdate.cancel();
	}

	@Override
	public void onWindowClosing(ClosingEvent event) {
		event.setMessage("ゲームプレイ中にウィンドウを閉じた場合\nレーティングにペナルティが加わります\nよろしいですか？");
	}

	@Override
	public void onClose(CloseEvent<Window> event) {
		final int newRating = UserData.getInstance().getRating() * 99 / 100;
		UserData.getInstance().setRating(newRating);
		UserData.getInstance().save();
	}
}
